package com.framework.service.impl;

import cn.dev33.satoken.util.SaResult;
import com.framework.entity.ChunkFile;
import com.framework.entity.MergeFile;
import com.framework.minio.MinioUtil;
import com.framework.repository.ChunkFileRepository;
import com.framework.repository.MergeFileRepository;
import com.framework.service.ChunkFileService;
import com.framework.utiles.FileHashed;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import javax.transaction.Transactional;
import java.io.IOException;
import java.security.NoSuchAlgorithmException;
import java.util.List;
import java.util.concurrent.*;

/**
 * @Description: 切片文件服务接口实现
 * @Author: JamesYao
 * @DateTime: 2024/1/17 0:29
 */
@Service
@RequiredArgsConstructor
@Transactional
@Slf4j
public class ChunkFileServiceImpl implements ChunkFileService {
    public static ThreadPoolExecutor customThreadPoolExecutor = new ThreadPoolExecutor(
            4, 500, 5000, TimeUnit.MILLISECONDS,
            new LinkedBlockingQueue<>(Integer.MAX_VALUE)
    );

    private final ChunkFileRepository chunkFileRepository;
    private final MergeFileRepository mergeFileRepository;
    private final MinioUtil minioUtil;
    private final FileHashed fileHashed;


    /**
     * 获取所有块文件列表
     *
     * @return 块文件列表
     */
    @Override
    public List<ChunkFile> getChunks() {
        return chunkFileRepository.findAll();
    }


    /**
     * 保存数据块
     *
     * @param chunkFile 数据块对象
     * @return 操作结果
     */
    @Override
    public SaResult saveChunk(ChunkFile chunkFile) {
        // 开线程
        Callable<ChunkFile> callable = () -> chunkFileRepository.save(chunkFile);
        Future<ChunkFile> futureResult = customThreadPoolExecutor.submit(callable);
        try {
            ChunkFile chunkFile1 = futureResult.get();
            log.info("分片文件对象为->{}", chunkFile1.getId());
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        } catch (ExecutionException e) {
            throw new RuntimeException(e);
        }

        return SaResult.ok();
    }

    /**
     * 上传文件的片段
     *
     * @param file      要上传的文件
     * @param chunkFile 文件片段信息
     * @return 上传结果
     */
    @Override
    public SaResult uploadChunk(MultipartFile file, ChunkFile chunkFile) throws NoSuchAlgorithmException, IOException, ExecutionException, InterruptedException {
        String hash = fileHashed.calculateSha256(file);
        String hash1 = chunkFile.getHash();
        if (!hash.equals(hash1)) {
            return SaResult.error("文件验证失败");
        }
        Callable<String> callable = () -> minioUtil.uploadReturnUrl(file, "public");
        Future<String> futureResult = customThreadPoolExecutor.submit(callable);

        String aPublic = futureResult.get();
        if (StringUtils.isBlank(aPublic)) {
            return SaResult.error("上传失败");
        }
        chunkFile.setPath(aPublic);
            return this.saveChunk(chunkFile);
    }


    /**
     * 断点续传上传文件的片段
     *
     * @param file      要上传的文件
     * @param chunkFile 文件片段信息
     * @return 上传结果
     */
    @Override
    public SaResult backUploadChunk(MultipartFile file, ChunkFile chunkFile, String sumHash) throws NoSuchAlgorithmException, IOException, ExecutionException, InterruptedException {
        String hash = fileHashed.calculateSha256(file);
        String hash1 = chunkFile.getHash();
        if (!hash.equals(hash1)) {
            return SaResult.error("文件验证失败");
        }
        Callable<String> callable = () -> minioUtil.uploadReturnUrl(file, "public");
        Future<String> futureResult = customThreadPoolExecutor.submit(callable);

        String aPublic = futureResult.get();
        if (StringUtils.isBlank(aPublic)) {
            return SaResult.error("上传失败");
        }
        List<MergeFile> mergeFiles =  mergeFileRepository.findByHash(sumHash);
        String fileId = chunkFile.getFileId();
        for (MergeFile mergeFile : mergeFiles) {
            String mergeFileId = mergeFile.getFileId();
            List<ChunkFile> chunkFiles = chunkFileRepository.findByFileId(mergeFileId);
            if (chunkFiles.isEmpty()){
                mergeFileRepository.deleteById(mergeFile.getId());
            }
            if (!chunkFiles.isEmpty()){
                fileId = mergeFile.getFileId();
            }
        }
        chunkFile.setFileId(fileId);
        chunkFile.setPath(aPublic);
        return this.saveChunk(chunkFile);
    }


}
